/*jslint maxerr: 50, indent: 4, browser: true, bitwise: true */
/*global tokenList */

/* More Or Less v1.4
 *
 * home: http://code.google.com/p/more-or-less/
 *
 * Add functionality that only displays the first few lines of a block of content
 * with a 'More/Less' link at the bottom, which when clicked, will expand or
 * contract the content. Written with cross-browser compatibility in mind and
 * now uses the DOMTokenList project for dealing with classList.
 *
 * http://code.google.com/p/domtokenlist/
 *
 * Copyright (C) 2013  Graham Fairweather (a.k.a: Xotic750)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

var moreLess = (function (tokenList) {
    "use strict";

    var toStringFN = {}.toString,
        moreLessClassListProperty = "classList",
        classNameProperty = "className",
        texts = {
            "continued": "[...]",
            "more": "more",
            "less": "less"
        };

    function addEvent(node, type, func) {
        var handler;

        if (node.addEventListener) {
            node.addEventListener(type, func, false);
        } else if (node.attachEvent) {
            node["e" + type + func] = func;
            node[type + func] = function () {
                node["e" + type + func](window.event);
            };

            node.attachEvent("on" + type, node[type + func]);
        } else {
            handler = node["on" + type];
            if (typeof handler === "function") {
                node["on" + type] = function (evt) {
                    handler(evt);
                    func(evt);
                };
            } else {
                node["on" + type] = func;
            }
        }
    }

    function addTokenList(element) {
        if (!element[moreLessClassListProperty]) {
            tokenList.addTokenList(element, classNameProperty, moreLessClassListProperty);
        }
    }

    function getElementsByClassName(node, className) {
        var elements = node.getElementsByTagName("*"),
            length = elements.length,
            array = [],
            i = 0,
            element;

        while (i < length) {
            element = elements[i];
            addTokenList(element);
            if (element[moreLessClassListProperty].contains(className)) {
                array.push(element);
            }

            i += 1;
        }

        return array;
    }

    function createElement(content) {
        var moreLess = document.createElement("div"),
            element = document.createElement("div");

        addTokenList(element);
        element[moreLessClassListProperty].add("more-block", "more-block-less");
        if (typeof content === "string") {
            element.innerHTML = content;
        } else {
            element.appendChild(content);
        }

        addTokenList(moreLess);
        moreLess[moreLessClassListProperty].add("more-less", "more-less-added");
        moreLess.appendChild(element);

        element = document.createElement("div");
        addTokenList(element);
        element[moreLessClassListProperty].add("more-less-continued");
        element.appendChild(document.createTextNode(texts.continued));
        moreLess.appendChild(element);

        element = document.createElement("div");
        addTokenList(element);
        element[moreLessClassListProperty].add("more-less-control");
        element.appendChild(document.createTextNode(texts.more));
        moreLess.appendChild(element);

        return moreLess;
    }

    function addMarkup() {
        var moreLesses = getElementsByClassName(document, "more-less"),
            length = moreLesses.length,
            i = 0,
            node,
            child,
            element;

        while (i < length) {
            node = moreLesses[i];
            addTokenList(node);
            if (!node[moreLessClassListProperty].contains("more-less-added")) {
                node[moreLessClassListProperty].add("more-less-added");
                child = node.children[0];
                addTokenList(child);
                child[moreLessClassListProperty].add("more-block-less");

                element = document.createElement("div");
                addTokenList(element);
                element[moreLessClassListProperty].add("more-less-continued");
                element.appendChild(document.createTextNode(texts.continued));
                node.appendChild(element);

                element = document.createElement("div");
                addTokenList(element);
                element[moreLessClassListProperty].add("more-less-control");
                element.appendChild(document.createTextNode(texts.more));
                node.appendChild(element);
            }

            i += 1;
        }
    }

    function bind() {
        addEvent(document, "click", function onClicked(evt) {
            var target = evt.target,
                parent = target.parentNode,
                moreBlock = parent.children[0],
                continueds = getElementsByClassName(parent, "more-less-continued"),
                length,
                continued,
                item;

            if (moreBlock.nodeName === "DIV" && moreBlock[moreLessClassListProperty].contains("more-block")) {
                length = continueds.length;
                item = 0;
                while (item < length) {
                    continued = continueds[item];
                    if (continued[moreLessClassListProperty].contains("more-less-continued-hide")) {
                        continued[moreLessClassListProperty].remove("more-less-continued-hide");
                        if (item === 0) {
                            moreBlock[moreLessClassListProperty].add("more-block-less");
                            target.firstChild.nodeValue = texts.more;
                        }
                    } else {
                        continued[moreLessClassListProperty].add("more-less-continued-hide");
                        if (item === 0) {
                            moreBlock[moreLessClassListProperty].remove("more-block-less");
                            target.firstChild.nodeValue = texts.less;
                        }
                    }

                    item += 1;
                }
            }
        });
    }

    function setTexts(object) {
        if (toStringFN.call(object) === "[object Object]") {
            var i;

            for (i in object) {
                if (object.hasOwnProperty(i)) {
                    if (texts[i] && typeof object[i] === "string") {
                        texts[i] = object[i];
                    }
                }
            }
        }
    }

    return {
        "setTexts": setTexts,
        "bind": bind,
        "addMarkup": addMarkup,
        "createElement": createElement
    };
}(tokenList));
